package com.ywk.base.concurrency.thread.demo;

import lombok.SneakyThrows;

/**
 * 买票案例:
 * 出现的问题:
 * 1. 相同票数出现多次；
 * 2. 出现了负票
 *
 * 补充
 * 锁池与等待池：
 * 锁池：假设线程A已经拥有了某个对象(注意:不是类)的锁，而其它的线程想要调用这个对象的某个 synchronized 方法(或者 synchronized块 )，
 * 由于这些线程在进入对象的 synchronized 方法之前必须先获得该对象的锁的拥有权，但是该对象的锁目前正被线程A拥有，所以这些线程就进入
 * 了该对象的锁池中，处在竞争锁的阶段。
 * 等待池：假设一个线程A调用了某个对象的 wait() 方法，线程A就会释放该对象的锁后，进入到了该对象的等待池中，等待池中的线程不会去竞争该对象的锁。
 * 只有当拥有锁的线程释放锁之前调用 notify() 或 notifyAll() 之后唤醒等待池的线程，此时等待池的线程才有资格去竞争锁。
 *
 * notify() 和 notifyAll() 的使用与区别：
 * 1.当有线程调用了对象的 notifyAll()方法（唤醒所有 wait 线程）或 notify()方法（只随机唤醒一个 wait 线程），被唤醒的的线程
 * 便会进入该对象的锁池中，锁池中的线程会去竞争该对象锁。也就是说，调用了 notify 后只有一个线程会由等待池进入锁池，而 notifyAll 会将该对象
 * 等待池内的所有线程移动到锁池中，等待锁竞争
 * 2.优先级高的线程竞争到对象锁的概率大，假若某线程没有竞争到该对象锁，它还会留在锁池中，唯有线程再次调用 wait()方法，它才会重新回
 * 到等待池中。而竞争到对象锁的线程则继续往下执行，直到执行完了 synchronized 代码块，它会释放掉该对象锁，这时锁池中的线程会继续竞争该对象锁。
 *
 * @Author zbh
 * @Date 2024/1/3
 */
public class SellTicketDemo implements Runnable{

    //定义一个成员变量表示有100张票
    private volatile int tickets=100;

    private Object lock=new Object();//声明一个锁（锁是任意的，但一定是唯一的）

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            synchronized(lock) {//锁要在死循环后面，让多个线程进来抢占锁（锁在前面的话就只有一个线程在出票了）
                if (tickets > 0) {
                    //通过sleep()方法来等待
                    Thread.sleep(100);//sleep()
                    if (tickets%5==0){
                        int i=tickets;
                        System.out.println(Thread.currentThread().getName() + "第" + tickets-- + "张票特殊处理中...");
                        lock.wait();
                        System.out.println(Thread.currentThread().getName() + "第" + i + "张票特殊处理完成");
                    }else {
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets-- + "张票");
                        lock.notifyAll();
                    }
                }else {
                    System.out.println("票已售空");
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        SellTicketDemo st = new SellTicketDemo();

        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
